#include "Level2.h"
#include "Level2Manager.h"
#include "Transformation.h"
#include "HUDtimer.h"
#include "Counter.h"
#include "ColorRGB.h"
#include "SimpleText.h"
#include "HUDlife.h"
#include "ScoreValue.h"
#include "Helpers.hpp"
#include "Logger.h"
#include "Exception.h"

using namespace game_engine;
using namespace game_engine::components;
using namespace game_engine::library;
using namespace game_engine::utils;

///////////////////////////////////////////////////////////////////////////////
// Constructors and Destructors
///////////////////////////////////////////////////////////////////////////////

Level2Manager::Level2Manager(void)
    : SceneManager(), timerValue(0.0f)
{
	slotTimerUpdated.AssignFunction(this, &Level2Manager::timerUpdated);
}

Level2Manager::Level2Manager(Level2 *parent)
    : SceneManager(parent), timerValue(0.0f)
{
	slotTimerUpdated.AssignFunction(this, &Level2Manager::timerUpdated);
}

Level2Manager::~Level2Manager(void)
{
}

///////////////////////////////////////////////////////////////////////////////
// Other public methods
///////////////////////////////////////////////////////////////////////////////

void Level2Manager::initialize(void) throw(...)
{
    // This is where all connections to signals are defined
    // IMPORTANT : all objects connecting to the addMovedObject slot, must have
    // a BoxCollider component
    vector<GameObject*> objs = objectsWithCollider();
    vector<GameObject*>::iterator i;
    for (i=objs.begin(); i!=objs.end(); i++)
    {
        Transformation *transf = dynamic_cast<Transformation*> 
            ((*i)->getComponent("Transformation"));
        slotMovedObject.Connect(transf->signalPositionUpdated);
    }

	// Connect to HUD timer
	HUDtimer *timer = dynamic_cast<HUDtimer*> (scene->getObjectByName("Time"));
	Counter *c = dynamic_cast<Counter*> (timer->getComponent("Counter"));
	slotTimerUpdated.Connect(c->signalValueUpdated);
}

const BOOL Level2Manager::update(void) throw(...)
{
	BOOL continueGame = true;

    // If any object has moved
    if (!movedObjects.empty())
    {
        // Get the list of moved objects which collide with the world
        vector<GameObject*> objToWorld;
        try
        {
            objToWorld = collisionArbiter.evaluateWorldCollisions(scene, 
                movedObjects);
        } catch (exception &e)
        {
            Logger::ERR(GAME_LOG, e.what());
            movedObjects.clear();
            throw RuntimeException(e.what());
        }

        // Carry out the appropriate actions with those objects
        vector<GameObject*>::iterator i;
        for (i=objToWorld.begin(); i<objToWorld.end(); i++)
        {
            processWorldCollision(*i);
        }

        // Clear list of moved objects
        movedObjects.clear();

        // Get the list of pairs of objects which collide with each other
        // All objects for which a BoxCollider had been defined must be analyzed
        vector<GameObject*> objs = objectsWithCollider();
        vector<pair<GameObject*, GameObject*>> objCollisions;
        try
        {
             objCollisions = collisionArbiter.evaluateInterCollisions(objs);
        } catch (exception &e)
        {
            Logger::ERR(GAME_LOG, e.what());
            throw RuntimeException(e.what());
        }

        // Carry out the appropriate actions with those objects
        vector<pair<GameObject*, GameObject*>>::iterator j;
        for (j=objCollisions.begin(); j<objCollisions.end(); j++)
        {
            BOOL bye = processObjectsCollision(*j);
            if (bye)
            {
                return false;
            }
        }

    } // if (!movedObjects.empty())

	// Process timer value
	if (timerValue < 0.9f)
	{
		continueGame = false;
	}
	else if (timerValue < 6.0f)
	{
		// Change timer color
		HUDtimer *timer = dynamic_cast<HUDtimer*> (scene->getObjectByName("Time"));
		timer->setTextColor(ColorRGB(255, 0, 0));
	}

    return continueGame;
}

void Level2Manager::finalize(void) throw(...)
{
    // This is where all connections to signals are destroyed
    slotMovedObject.DisconnectAll();
	slotTimerUpdated.DisconnectAll();
}

///////////////////////////////////////////////////////////////////////////////
// Slots
///////////////////////////////////////////////////////////////////////////////

INT32 Level2Manager::timerUpdated(FLOAT32 newValue)
{
	timerValue = newValue;
	return 0;
}

///////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////

const BOOL Level2Manager::processObjectsCollision(pair<GameObject*, GameObject*> duple)
{
    BOOL gameover = false;

    // Collision between player and Enemy
    if ((((duple.first)->getTag() == "Player") && ((duple.second)->getTag() == "Enemy")) ||
        (((duple.first)->getTag() == "Enemy") && ((duple.second)->getTag() == "Player")))
    {
        // Same behaviour as of world collision
        processWorldCollision(duple.first);
        processWorldCollision(duple.second);
    }

    // Collision between player and Star
    else if ((((duple.first)->getTag() == "Player") && ((duple.second)->getTag() == "Star")) ||
        (((duple.first)->getTag() == "Star") && ((duple.second)->getTag() == "Player")))
    {
        // Increment score
        ScoreValue *score = dynamic_cast<ScoreValue*> (scene->getObjectByName("PlayerScoreValue"));
        score->incrementScore(STAR_SCORE, 0);

        // Remove Star object
        if ((duple.second)->getTag() == "Star")
        {
            //scene->removeObject(duple.second);
            Level2 *lvl2 = dynamic_cast<Level2*> (scene);
            lvl2->removeObject(((duple.second)->getName()).c_str(), 0);
        }
        else
        {
            //scene->removeObject(duple.first);
            Level2 *lvl2 = dynamic_cast<Level2*> (scene);
            lvl2->removeObject(((duple.first)->getName()).c_str(), 0);
        }
    }

    return gameover;
}
